5.Go语言之配置文件读取学习记录

您所在的位置:网站首页 mapstructure go语言 5.Go语言之配置文件读取学习记录

5.Go语言之配置文件读取学习记录

2023-05-05 06:00| 来源: 网络整理| 查看: 265

[TOC]

0x00 前言简述

描述: 作为开发者相信对应用程序的配置文件并不陌生吧,例如 Java Spring Boot 里的 class 目录中程序配置,当然go语言相关项目也是可以根据配置文件的格式内容进行读取的,常规的配置文件格式有 json、ini、yaml (个人推荐)、properties 等,我们可以使用其为程序配置一些初始化的可变参数,例如 数据库字符串链接以及认证密码等等。

好,下面作者将依次从json、ini、以及yaml 等顺序进行讲解。

0x01 常用模块encoding/json 模块 - JSON 配置文件解析

config.json 配置文件示例

{ "app": { "app_name": "hello-gin", "app_mode": "dev", "app_host": "localhost", "app_port": "8080", "app_secret": "weiyigeek.top" }, "log":{ "log_name": "app", "log_path": "/logs", "log_age": "180", "log_rotation_time": "24" }, "db": { "mysql": { "mysql_user": "root", "mysql_pass": "123456", "mysql_addr": "localhost", "mysql_port": "3306", "mysql_database": "test" }, "redis": { "redis_addr": "localhost", "redis_port": "6379", "redis_database": "9", "redis_pass": "123456" } } }

LoadJSONConfig函数进行JSON配置文件读取

package config import ( "encoding/json" "log" "os" ) // 读取 JSON 配置文件 // JSON 配置文件结构体 type AppJSONConfig struct { AppName string `json:"app_name"` AppMode string `json:"app_mode"` AppHost string `json:"app_host"` AppPort string `json:"app_port"` AppSecret string `json:"app_secret"` } type LogJSONConfig struct { LogPath string `json:"log_path"` LogName string `json:"log_name"` LogAge string `json:"log_age"` LogRotationTime string `json:"log_rotation_time"` } type DbMySQLJSONConfig struct { User string `json:"mysql_user"` Pass string `json:"mysql_pass"` Addr string `json:"mysql_addr"` Port string `json:"mysql_port"` Database string `json:"mysql_database"` } type DbRedisJSONConfig struct { Addr string `json:"redis_addr"` Port string `json:"redis_port"` Pass string `json:"redis_pass"` Database string `json:"redis_database"` } type DbJSONConfig struct { Mysql DbMySQLJSONConfig `json:"mysql"` Redis DbRedisJSONConfig `json:"redis"` } type JSONConfig struct { App AppJSONConfig `json:"app"` Log LogJSONConfig `json:"log"` Db DbJSONConfig `json:"db"` } func LoadJSONConfig(path string) *JSONConfig { // 定义局部变量 var Config JSONConfig // 打开配置文件 f, err := os.Open(path) if err != nil { log.Printf("Open config file failed!") panic(err) } // 程序结束时关闭打开的文件 defer f.Close() // NewDecoder创建一个从file读取并解码json对象的*Decoder,解码器有自己的缓冲,并可能超前读取部分json数据。 decoder := json.NewDecoder(f) // Decode从输入流读取下一个json编码值并保存在v指向的值里(关键点) decode_err := decoder.Decode(&Config) if err != nil { panic(decode_err) } return &Config }

函数执行入口:

// 声明当前文件属于哪个包,如果是主文件,则写成main package main // 导入gin包 import ( "fmt" "hello-gin/util/config" ) func main() { conf := config.LoadJSONConfig() fmt.Print(conf, "\n") fmt.Println("----------------------------------") fmt.Println("应用名称", conf.App.AppName) fmt.Println("应用密钥", conf.App.AppSecret) }

执行结果:

> go run .\main.go &{{hello-gin dev localhost 8080 weiyigeek.top} {/logs app debug} {{root 123456 localhost 3306 test} {localhost 6379 123456 9}}} ---------------------------------- 应用名称 hello-gin 应用密钥 weiyigeek.topgopkg.in/ini.v1 模块 - ini 配置文件解析

在Go中读取INI文件,我们可以使用名为go-ini的第三方库(a third-party library),它是一个非常方便、高效的go配置文件操作库。

模块下载: go get -v -u gopkg.in/ini.v1

config.ini 配置文件示例:

[app] name="hellogin" mode="dev" host="localhost" port=8080 secret="weiyigeek" [log] name="ginDemo" path="D:/Study/Project/Go/hello-gin/logs" maxage=180 rotation_time=24 rotation_size=100 [mysql] host=192.168.2.2 port=3306 user=weiyigeek pass=123456 database=weiyigeek [redis] host=192.168.2.6 port=6379 pass=weiyigeek.top database=10

LoadINIConfig 读取ini配置文件函数

package config import ( "fmt" "gopkg.in/ini.v1" ) // 创建ini配置文件中相关的字段结构体 type AppINIConfig struct { AppName string `ini:"name"` AppMode string `ini:"mode"` AppHost string `ini:"host"` AppPort int `ini:"port"` AppSecret string `ini:"secret"` } type LogINIConfig struct { LogPath string `ini:"path"` LogName string `ini:"name"` LogAge int `ini:"age"` LogRotationTime int `ini:"rotation_time"` LogRotationSize int `ini:"rotation_size"` } type DbMysqlINIConfig struct { User string `ini:"user"` Pass string `ini:"pass"` Host string `ini:"addr"` Port int `ini:"port"` Database string `ini:"database"` } type DbRedisINIConfig struct { Host string `ini:"addr"` Port int `ini:"port"` Pass string `ini:"pass"` Database string `ini:"database"` } type INIConfig struct { App AppINIConfig `ini:"app"` Log LogINIConfig `ini:"log"` DbMysql DbMysqlINIConfig `ini:"mysql"` DbRedis DbMysqlINIConfig `ini:"redis"` } func LoadINIConfig(path string) *INIConfig { // 0.实例化结构体 config := &INIConfig{} // 1.加载配置文件 cfg, err := ini.Load(path) if err != nil { fmt.Println("配置文件读取错误,请检查文件路径:", err) panic(err) } // 2.读取配置文件各章节下的KV配置值,并设定默认默认值。 config.App.AppName = cfg.Section("app").Key("name").String() config.App.AppMode = cfg.Section("app").Key("mode").MustString("dev") config.App.AppHost = cfg.Section("app").Key("host").MustString("localhost") config.App.AppPort = cfg.Section("app").Key("port").MustInt(8080) config.App.AppSecret = cfg.Section("app").Key("secret").String() config.Log.LogName = cfg.Section("log").Key("name").String() config.Log.LogPath = cfg.Section("log").Key("path").String() config.Log.LogAge = cfg.Section("log").Key("maxage").MustInt(180) config.Log.LogRotationSize = cfg.Section("log").Key("rotation_time").MustInt(24) config.Log.LogRotationTime = cfg.Section("log").Key("rotation_size").MustInt(100) config.DbMysql.Host = cfg.Section("mysql").Key("host").String() config.DbMysql.Port = cfg.Section("mysql").Key("port").MustInt(3306) config.DbMysql.User = cfg.Section("mysql").Key("user").String() config.DbMysql.Pass = cfg.Section("mysql").Key("pass").String() config.DbMysql.Database = cfg.Section("mysql").Key("database").String() config.DbRedis.Host = cfg.Section("redis").Key("host").String() config.DbRedis.Port = cfg.Section("redis").Key("port").MustInt(6379) config.DbRedis.Pass = cfg.Section("redis").Key("pass").String() config.DbRedis.Database = cfg.Section("redis").Key("database").String() return config }

main 函数入口调用

// 声明当前文件属于哪个包,如果是主文件,则写成main package main // 导入gin包 import ( "fmt" "hello-gin/util/config" ) func main() { confINI := config.LoadINIConfig("configs/config.ini") fmt.Println(confINI) fmt.Println("App : ", confINI.App) fmt.Println("Log : ", confINI.Log) fmt.Println("Database :", confINI.DbRedis.Database) }

执行结果:

go run .\main.go &{{hellogin dev localhost 8080 weiyigeek} {D:/Study/Project/Go/hello-gin/logs ginDemo 180 100 24} {weiyigeek 123456 192.168.2.2 3306 weiyigeek} { weiyigeek.top 192.168.2.6 6379 10}} App : {hellogin dev localhost 8080 weiyigeek} Log : {D:/Study/Project/Go/hello-gin/logs ginDemo 180 100 24} Database : 10gopkg.in/yaml.v3 模块 - yaml 配置文件解析

描述: gopkg.in/yaml.v3 包使Go程序能够轻松地对yaml值进行编码和解码, 它是作为juju项目的一部分在Canonical中开发的,基于著名的libyaml C库的纯Go端口,可以快速可靠地解析和生成YAML数据。

项目地址: https://github.com/go-yaml/yaml

模块包下载: go get -v -u gopkg.in/yaml.v3

config.yaml 配置文件示例

app: name: "hellogin" mode: "dev" host: "localhost" port: "8080" secret: "weiyigeek" log: name: "ginDemo" path: "D:/Study/Project/Go/hello-gin/logs" maxage: 180 rotation_time: 24 rotation_size: 100 db: mysql: host: 192.168.2.2 port: 3306 user: weiyigeek pass: 123456 database: weiyigeek redis: host: 192.168.2.6 port: 3306 pass: weiyigeek.top database: 10 user: user: - weiyigeek - geeker mqtt: host: 192.168.2.7:1883 username: weiyigeek password: weiyigeek.top

LoadYAMLConfig 函数读取yaml配置文件

package config import ( "fmt" "io/ioutil" "log" "gopkg.in/yaml.v3" ) // 读取yaml配置文件 // YAML 配置文件结构体 type AppYAMLConfig struct { AppName string `yaml:"name"` AppMode string `yaml:"mode"` AppHost string `yaml:"host"` AppPort string `yaml:"port"` AppSecret string `yaml:"secret"` } type LogYAMLConfig struct { LogPath string `yaml:"path"` LogName string `yaml:"name"` LogAge string `yaml:"age"` LogRotationTime string `yaml:"rotation_time"` LogRotationSize string `yaml:"rotation_size"` } type DbMySQLYAMLConfig struct { User string `yaml:"user"` Pass string `yaml:"pass"` Addr string `yaml:"addr"` Port string `yaml:"port"` Database string `yaml:"database"` } type DbRedisYAMLConfig struct { Addr string `yaml:"addr"` Port string `yaml:"port"` Pass string `yaml:"pass"` Database string `yaml:"database"` } type DbYAMLConfig struct { Mysql DbMySQLYAMLConfig `yaml:"mysql"` Redis DbRedisYAMLConfig `yaml:"redis"` } type MqttYAMLconfig struct { Host string `yaml:"host"` Username string `yaml:"username"` Password string `yaml:"password"` } type UserYAMLconfig struct { User []string `yaml:"user"` Mqtt MqttYAMLconfig `yaml:"mqtt"` } type YAMLConfig struct { App AppYAMLConfig `yaml:"app"` Log LogYAMLConfig `yaml:"log"` Db DbYAMLConfig `yaml:"db"` User UserYAMLconfig `yaml:"user"` } func LoadYAMLConfig(path string) *YAMLConfig { // 定义局部变量 config := &YAMLConfig{} // 打开并读取yaml配置文件 yamlFile, err := ioutil.ReadFile(path) if err != nil { log.Printf("Open config.yaml failed!") panic(err) } // 使用yaml中Unmarshal方法,解析yaml配置文件并绑定定义的结构体 err = yaml.Unmarshal(yamlFile, config) if err != nil { log.Printf("yaml config file Unmarsha failed!") panic(err) } return config }

入口函数:

// 声明当前文件属于哪个包,如果是主文件,则写成main package main // 导入gin包 import ( "fmt" "hello-gin/util/config" ) func main() { confYAML := config.LoadYAMLConfig("configs/config.yaml") fmt.Print(confYAML, "\n") fmt.Println("--------------------------------------------------------------") fmt.Println("应用用户", confYAML.User.User) fmt.Println("应用名称", confYAML.App.AppName) fmt.Println("应用密钥", confYAML.App.AppSecret) fmt.Println("MySQL账号密码:", confYAML.Db.Mysql.User, confYAML.Db.Mysql.Pass) }

执行结果:

&{{hellogin dev localhost 8080 weiyigeek} {D:/Study/Project/Go/hello-gin/logs ginDemo 24 100} {{weiyigeek 123456 3306 weiyigeek} { 3306 weiyigeek.top 10}} {[weiyigeek geeker] {192.168.2.7:1883 weiyigeek weiyigeek.top}}} -------------------------------------------------------------- 应用用户 [weiyigeek geeker] 应用名称 hellogin 应用密钥 weiyigeek原生map结构 - properties 配置文件解析

properties 配置文件

appName=hellogin appMode=dev appHost=localhost appPort=8080 appSecret="WeiyiGeek" logName="ginDemo" logPath="D:/Study/Project/Go/hello-gin/logs" logMaxage=180 logRotationTime=24 logRotationSize=100

LoadPropConfig 函数读取properties配置文件

package config import ( "bufio" "fmt" "io" "os" "strings" ) var properties = make(map[string]string) func LoadPropConfig(path string) map[string]string { propFile, err := os.OpenFile(path, os.O_RDONLY, 0666) if err != nil { fmt.Println("打开 config.properties 配置文件失败!") panic(err) } propReader := bufio.NewReader(propFile) for { prop, err := propReader.ReadString('\n') if err != nil { if err == io.EOF { break } } // 此处注意不要出现Yoda conditions问题 if (len(prop) == 0) || (prop == "\n") { continue } properties[strings.Replace(strings.Split(prop, "=")[0], " ", "", -1)] = strings.Replace(strings.Split(prop, "=")[1], " ", "", -1) } return properties }

函数调用主入口

// 声明当前文件属于哪个包,如果是主文件,则写成main package main // 导入gin包 import ( "fmt" "hello-gin/util/config" ) func main() { properties := config.LoadPropConfig("configs/config.properties") fmt.Println(properties) fmt.Print("AppName : ", properties["appName"]) fmt.Println("LogPath : ", properties["logPath"]) }

执行结果:

$ go run .\main.go map[appHost:localhost appMode:dev appName:hellogin appPort:8080 appSecret:"WeiyiGeek" logMaxage:180 logName:"ginDemo" logPath:"D:/Study/Project/Go/hello-gin/logs" logRotationTime:24 ] AppName : hellogin LogPath : "D:/Study/Project/Go/hello-gin/logs"spf13/viper 模块 - 配置文件解析终结者

描述: 在前面实践中作者分别用了三种模块包以原生包针对四种不同配置的文件,那到底有没有引入一个包就可以全部解析的呢? 既然这么问,那答案显然是可以的, 那就是今天的主人公 viper 项目地址: github.com/spf13/viper

viper 适用于Go应用程序(包括[Twelve-Factor App)的完整配置解决方案,它被设计用于在应用程序中工作,并且可以处理所有类型的配置需求和格式,它支持以下特性:

设置默认值以及显式配置值从JSON、TOML、YAML、HCL、envfile和Java properties格式的配置文件读取配置信息 var SupportedExts = []string{“json”, “toml”, “yaml”, “yml”, “properties”, “props”, “prop”, “hcl”, “tfvars”, “dotenv”, “env”, “ini”} 实时监控和重新读取配置文件(可选)从环境变量中读取从远程配置系统(etcd或Consul)读取并监控配置变化 var SupportedRemoteProviders = []string{“etcd”, “etcd3”, “consul”, “firestore”} 从命令行参数读取配置从buffer读取配置

Viper 优先级

显示调用Set设置值命令行参数(flag)环境变量配置文件key/value存储默认值

PS: 目前Viper配置的键(Key)是大小写不敏感的。

模块下载安装: go get github.com/spf13/viper

常用函数

Get(key string) : interface{} GetBool(key string) : bool GetFloat64(key string) : float64 GetInt(key string) : int GetIntSlice(key string) : []int GetString(key string) : string GetStringMap(key string) : map[string]interface{} GetStringMapString(key string) : map[string]string GetStringSlice(key string) : []string GetTime(key string) : time.Time GetDuration(key string) : time.Duration IsSet(key string) : bool AllSettings() : map[string]interface{}

温馨提示: 每一个Get方法在找不到值的时候都会返回零值, 为了检查给定的键是否存在,提供了IsSet()方法.

参考地址: https://github.com/spf13/viper/blob/master/README.md

常规使用

// # 如果没有通过配置文件、环境变量、远程配置或命令行标志(flag)设置键,则默认值非常有用。 viper.SetDefault("ContentDir", "content") viper.SetDefault("LayoutDir", "layouts") viper.SetDefault("Taxonomies", map[string]string{"tag": "tags", "category": "categories"}) // # Viper支持JSON、TOML、YAML、HCL、envfile和Java properties格式的配置文件 // 方式1 viper.SetConfigFile("config.yaml") // 指定配置文件路径 viper.AddConfigPath("/etc/appname/") // 查找配置文件所在的路径 // 方式2 viper.SetConfigName("config") // 配置文件名称(无扩展名) viper.SetConfigType("yaml") // 如果配置文件的名称中没有扩展名,则需要配置此项 viper.AddConfigPath("/etc/appname/") // 查找配置文件所在的路径 viper.AddConfigPath("$HOME/.appname") // 多次调用以添加多个搜索路径 viper.AddConfigPath(".") // 还可以在工作目录中查找配置 // # 通过 ReadInConfig 函数,寻找配置文件并读取,操作的过程中可能会发生错误,如配置文件没找到,配置文件的内容格式不正确等; if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { // 配置文件未找到错误;如果需要可以忽略 } else { // 配置文件被找到,但产生了另外的错误 } } else { // 配置文件找到并成功解析 } // # Viper 覆盖设置 viper.Set("Verbose", true) viper.Set("LogFile", LogFile) // # 通过 ReadInConfig 函数,寻找配置文件并读取,操作的过程中可能会发生错误,如配置文件没找到,配置文件的内容格式不正确等; fmt.Println(viper.Get("mysql")) // map[port:3306 url:127.0.0.1] fmt.Println(viper.Get("mysql.url")) // 127.0.0.1 // # 使用viper函数获取嵌套的键的值 { "host": { "address": "localhost", "port": 5799 }, "datastore": { "mysql": { "host": "127.0.0.1", "port": 3306 } } } viper.GetString("datastore.mysql.host") // (返回 "127.0.0.1") // # 获取子树值 app: cache1: max-items: 100 item-size: 64 cache2: max-items: 200 item-size: 80 subv := viper.Sub("app.cache1") // # viper加载完配置信息后使用结构体变量保存配置信息 type Config struct { Port int `mapstructure:"port"` Version string `mapstructure:"version"` } var Conf = new(Config) // 将读取的配置信息保存至全局变量Conf if err := viper.Unmarshal(Conf); err != nil { panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err)) } // # 使用WatchConfig监控配置文件变化 viper.WatchConfig() // 注意!!!配置文件发生变化后要同步到全局变量Conf viper.OnConfigChange(func(in fsnotify.Event) { fmt.Println("配置文件被修改了") if err := viper.Unmarshal(Conf); err != nil { panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err)) } })

示例1.Viper支持Cobra库中使用的Pflag绑定并输出

package main import ( "flag" "fmt" "github.com/spf13/pflag" "github.com/spf13/viper" ) func main() { // 使用标准库 "flag" 包 flag.Int("flagname", 1234, "help message for flagname") // pflag 包可以通过导入这些 flags 来处理flag包定义的flags, pflag 包可以通过导入这些 flags 来处理flag包定义的flags pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() // viper 绑定到标志 viper.BindPFlags(pflag.CommandLine) // 从 viper 检索值 i := viper.GetInt("flagname") fmt.Println(i) // 1234 }

示例2.使用viper读取yaml并存放在结构体中 configs\prod.yaml 配置文件

app: name: "hellogin" mode: "dev" host: "localhost" port: "8080" secret: "weiyigeek" version: "v1.2.0" log: name: "ginDemo" path: "D:/Study/Project/Go/hello-gin/logs" maxage: 180 rotation_time: 24 rotation_size: 100 db: mysql: host: 192.168.2.2 port: 3306 user: weiyigeek pass: 123456 database: weiyigeek redis: host: 192.168.2.6 port: 3306 pass: weiyigeek.top database: 10

代码示例:

// go get -v -u gopkg.in/yaml.v3 package main import ( "fmt" "github.com/fsnotify/fsnotify" "github.com/spf13/viper" ) // YAML 配置文件KV结构体 type AppYAMLConfig struct { AppName string `mapstructure:"name"` AppMode string `mapstructure:"mode"` AppHost string `mapstructure:"host"` AppPort string `mapstructure:"port"` AppSecret string `mapstructure:"secret"` AppVersion string `mapstructure:"version"` } type LogYAMLConfig struct { LogPath string `mapstructure:"path"` LogName string `mapstructure:"name"` LogAge string `mapstructure:"age"` LogRotationTime string `mapstructure:"rotation_time"` LogRotationSize string `mapstructure:"rotation_size"` } type DbMySQLYAMLConfig struct { User string `mapstructure:"user"` Pass string `mapstructure:"pass"` Addr string `mapstructure:"addr"` Port string `mapstructure:"port"` Database string `mapstructure:"database"` } type DbRedisYAMLConfig struct { Addr string `mapstructure:"addr"` Port string `mapstructure:"port"` Pass string `mapstructure:"pass"` Database string `mapstructure:"database"` } type DbYAMLConfig struct { Mysql DbMySQLYAMLConfig `mapstructure:"mysql"` Redis DbRedisYAMLConfig `mapstructure:"redis"` } type MqttYAMLconfig struct { Host string `mapstructure:"host"` Username string `mapstructure:"username"` Password string `mapstructure:"password"` } type YAMLConfig struct { App AppYAMLConfig `mapstructure:"app"` Log LogYAMLConfig `mapstructure:"log"` Db DbYAMLConfig `mapstructure:"db"` } // viper对象示例化与配置文件读取 func NewConfig(name string) *viper.Viper { // 设置实例化viper对象 vp := viper.New() // 设置配置文件名,没有后缀 vp.SetConfigName(name) // 设置读取文件格式为: yaml vp.SetConfigType("yaml") // 设置配置文件目录(可以设置多个,优先级根据添加顺序来) vp.AddConfigPath("./configs/") // 读取配置文件并解析 if err := vp.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { fmt.Printf("配置文件未找到!%v\n", err) return nil } else { fmt.Printf("找到配置文件,但是解析错误,%v\n", err) return nil } } // 绑定返回结构体对象 return vp } func main() { // 实例化 vp 对象 vp := NewConfig("prod") // 获取指定key值 fmt.Println("Version : ", vp.Get("app.version")) // 获取指定节值返回map数据类型 fmt.Println(vp.GetStringMap("app")) v := vp.GetStringMap("app") fmt.Println("version : ", v["version"], "\n") // 将获取到的数据绑定到结构体 conf := new(YAMLConfig) if err := vp.Unmarshal(conf); err != nil { fmt.Printf("解析错误: %v\n", err) panic(err) } fmt.Println(conf, "\n") // 监控配置文件的变化 // # 使用WatchConfig监控配置文件变化 vp.WatchConfig() // 注意!!!配置文件发生变化后要同步到全局变量Conf vp.OnConfigChange(func(in fsnotify.Event) { fmt.Println("配置文件被修改了") if err := vp.Unmarshal(conf); err != nil { panic(fmt.Errorf("unmarshal conf failed, err:%s \n", err)) } }) fmt.Println(vp) }

执行结果:

go run .\main.go Version : v1.2.0 map[host:localhost mode:dev name:hellogin port:8080 secret:weiyigeek version:v1.2.0] version : v1.2.0 &{{hellogin dev localhost 8080 weiyigeek v1.2.0} {D:/Study/Project/Go/hello-gin/logs ginDemo 24 100} {{weiyigeek 123456 3306 weiyigeek} { 3306 weiyigeek.top 10}}} &{. [D:\Study\Project\Go\hello-gin\configs] 0x1235c48 [] prod D:\Study\Project\Go\hello-gin\configs\prod.yaml yaml 420 {false false false false false false false false false false false false false false false [] false 0 false false} false false map[app:map[host:localhost mode:dev name:hellogin port:8080 secret:weiyigeek version:v1.2.0] db:map[mysql:map[database:weiyigeek host:192.168.2.2 pass:123456 port:3306 user:weiyigeek] redis:map[database:10 host:192.168.2.6 pass:weiyigeek.top port:3306]] log:map[maxage:180 name:ginDemo path:D:/Study/Project/Go/hello-gin/logs rotation_size:100 rotation_time:24]] map[] map[] map[] map[] map[] map[] false 0xfa25a0 {} 0xc000066860 0xc000066880}


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3